package com.yunkeji.api.auth.processor.login;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yunkeji.api.auth.bo.ToLoginBo;
import com.yunkeji.api.auth.processor.service.LoginFactory;
import com.yunkeji.api.auth.processor.service.LoginStrategy;
import com.yunkeji.api.auth.service.AuthService;
import com.yunkeji.api.auth.vo.ToLoginVo;
import com.yunkeji.common.exception.AppServiceException;
import com.yunkeji.common.utils.EncryptUtils;
import com.yunkeji.common.utils.ServletUtils;
import com.yunkeji.config.properties.ShanYanProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 一键登录
 *
 * @author 老张
 */
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class OneTouchLogin implements InitializingBean, LoginStrategy {
    private final AuthService authService;
    private static String EVN = SpringUtil.getActiveProfile();
    private final ShanYanProperties shanYanProperties;

    @Override
    public ToLoginVo execute(ToLoginBo bo) {
        if (StrUtil.isEmpty(bo.getOneTouchToken())) {
            throw AppServiceException.paramException("参数校验异常");
        }
        String mobile = decrypt(bo.getOneTouchToken());
        bo.setPhone(mobile);
        return authService.toLogin(bo, false);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LoginFactory.put(ToLoginBo.LoginType.一键登录, this);
    }

    /**
     * 解密
     *
     * @author 老张
     */
    private String decrypt(String token) {
        String branch = ServletUtils.getRequest().getHeader("branch");
        String appId, appKey;
        // 应用对应的闪验APPID
        // 应用对应的闪验APPKEY
        // 3开头的branch是另一个客户端，1开头则是爱洞，应用不同的appid和appkey
        if(branch.startsWith("3")){
            appId = shanYanProperties.getAppidTwo();
            appKey = shanYanProperties.getAppkeyTwo();
        } else {
            appId = shanYanProperties.getAppid();
            appKey = shanYanProperties.getAppkey();
        }
        // 手机号加解密方式 0 AES 1 RSA , 可以不传，不传则手机号解密直接使用AES解密
        String encryptType = "0";
        // 从SDK获取的token参数
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("token", token);
            params.put("appId", appId);
            params.put("encryptType", encryptType); // 可以不传，不传则解密直接使用AES解密
            params.put("sign", SignUtils.getSign(params, appKey));
            String post = HttpUtil.post(shanYanProperties.getGatway(), params);
            JSONObject jsonObject = JSONUtil.parseObj(post);
            if (null != jsonObject) {
                log.info("response:{}", jsonObject.toStringPretty());
                String code = jsonObject.getStr("code"); // 返回码 200000为成功
                if ("200000".equals(code)) {
                    JSONObject dataObj = jsonObject.getJSONObject("data");
                    String mobile = dataObj.getStr("mobileName");
                    if ("0".equals(encryptType)) {
                        String key = SignUtils.getMD5Code(appKey);
                        mobile = SignUtils.decrypt(mobile, key.substring(0, 16), key.substring(16));
                    } else if ("1".equals(encryptType)) {
                        mobile =
                                EncryptUtils.decryptByRsa(
                                        mobile, shanYanProperties.getPrivateKey());
                    }
                    log.info("mobile:{}", mobile);
                    return mobile;
                }
            }
        } catch (Exception e) {
            log.error("一键登录失败:{}", e);
            throw AppServiceException.paramException("登录失败,请稍后再试");
        }
        throw AppServiceException.paramException("登录失败,请稍后再试");
    }

    public static class SignUtils {
        private static final String charset = "UTF-8";
        // 全局数组
        private static final String[] strDigits = {
            "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g",
            "h", "j", "?", "~"
        };

        public static byte[] hexToBytes(String str) {
            if (str == null) {
                return null;
            } else {
                char[] hex = str.toCharArray();
                int length = hex.length / 2;
                byte[] raw = new byte[length];

                for (int i = 0; i < length; ++i) {
                    int high = Character.digit(hex[i * 2], 16);
                    int low = Character.digit(hex[i * 2 + 1], 16);
                    int value = high << 4 | low;
                    if (value > 127) {
                        value -= 256;
                    }

                    raw[i] = (byte) value;
                }

                return raw;
            }
        }
        /** Description: AES解密 */
        public static String decrypt(String sSrc, String sKey, String siv) throws Exception {
            try {
                if (sSrc == null || sSrc.length() == 0) {
                    return null;
                }
                if (sKey == null) {
                    throw new Exception("decrypt key is null");
                }
                if (sKey.length() != 16) {
                    throw new Exception("decrypt key length error");
                }
                byte[] Decrypt = hexToBytes(sSrc);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes(charset), "AES");
                IvParameterSpec iv =
                        new IvParameterSpec(siv.getBytes(charset)); // new IvParameterSpec(getIV());
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); // 使用解密模式初始化 密
                return new String(cipher.doFinal(Decrypt), charset);
            } catch (Exception ex) {
                throw new Exception("decrypt errot", ex);
            }
        }

        public static String getSign(Map<String, Object> requestMap, String appKey) {
            return hmacSHA256Encrypt(requestMap2Str(requestMap), appKey);
        }

        private static String hmacSHA256Encrypt(String encryptText, String encryptKey) {
            byte[] result = null;
            try {
                // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
                SecretKeySpec signinKey =
                        new SecretKeySpec(encryptKey.getBytes("UTF-8"), "HmacSHA256");
                // 生成一个指定 Mac 算法 的 Mac 对象
                Mac mac = Mac.getInstance("HmacSHA256");
                // 用给定密钥初始化 Mac 对象
                mac.init(signinKey);
                // 完成 Mac 操作
                byte[] rawHmac = mac.doFinal(encryptText.getBytes("UTF-8"));
                return bytesToHexString(rawHmac);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        private static String requestMap2Str(Map<String, Object> requestMap) {
            String[] keys = requestMap.keySet().toArray(new String[0]);
            Arrays.sort(keys);
            StringBuilder stringBuilder = new StringBuilder();
            for (String str : keys) {
                if (!str.equals("sign")) {
                    stringBuilder.append(str).append(requestMap.get(str));
                }
            }
            return stringBuilder.toString();
        }

        public static final String bytesToHexString(byte[] bArray) {
            StringBuffer sb = new StringBuffer(bArray.length);

            for (int i = 0; i < bArray.length; ++i) {
                String sTemp = Integer.toHexString(255 & bArray[i]);
                if (sTemp.length() < 2) {
                    sb.append(0);
                }

                sb.append(sTemp.toUpperCase());
            }

            return sb.toString();
        }

        public static String getMD5Code(String strObj) {
            String resultString = null;
            try {
                resultString = new String(strObj);
                MessageDigest md = MessageDigest.getInstance("MD5");
                // md.digest() 该函数返回值为存放哈希值结果的byte数组
                resultString = byteToString(md.digest(strObj.getBytes("UTF-8")));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return resultString;
        }
        // 转换字节数组为16进制字串
        private static String byteToString(byte[] bByte) {
            StringBuffer sBuffer = new StringBuffer();
            for (int i = 0; i < bByte.length; i++) {
                sBuffer.append(byteToArrayString(bByte[i]));
            }
            return sBuffer.toString();
        }

        // 返回形式为数字跟字符串
        private static String byteToArrayString(byte bByte) {
            int iRet = bByte;
            if (iRet < 0) {
                iRet += 256;
            }
            int iD1 = iRet / 16;
            int iD2 = iRet % 16;
            return strDigits[iD1] + strDigits[iD2];
        }
    }
}
